package br.com.mobprovas.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.id.IdentityGenerator.GetGeneratedKeysDelegate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.opensymphony.oscache.base.FinalizationException;


public abstract class GenericDAOImpl<T, ID extends Serializable> extends
		HibernateDaoSupport implements GenericDAO<T, ID> {

	private static Log LOG = LogFactory.getLog(GenericDAOImpl.class);

	private Class persistentClass;

	@SuppressWarnings("unchecked")
	public GenericDAOImpl() {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}
	
	
	public Class<T> getPersistentClass() {
		return this.persistentClass;
	}

	public T update(T entity) {
		try {
			this.getHibernateTemplate().merge(entity);
			return entity;
		} catch (final HibernateException ex) {
			ex.printStackTrace();
			GenericDAOImpl.LOG.error(ex);
			throw convertHibernateAccessException(ex);
		}
	}

	public void delete(T entity) {
		try {
			this.getHibernateTemplate().delete(entity);
		} catch (final HibernateException ex) {
			GenericDAOImpl.LOG.error(ex);
			throw convertHibernateAccessException(ex);
		}

	}

	public List<T> listByCriteria(Criterion... criterion) {
		try {
			Criteria crit = this.getHibernateTemplate().getSessionFactory()
					.getCurrentSession().createCriteria(getPersistentClass());
			for (Criterion c : criterion) {
				crit.add(c);
			}
			return crit.list();
		} catch (final HibernateException ex) {
			GenericDAOImpl.LOG.error(ex);
			ex.printStackTrace();
			throw convertHibernateAccessException(ex);
		}
	}
	public T findByCriteria(Criterion... criterion) {
		try {
			Criteria crit = this.getHibernateTemplate().getSessionFactory()
					.getCurrentSession().createCriteria(getPersistentClass());
			for (Criterion c : criterion) {
				crit.add(c);
			}
			return (T) crit.uniqueResult();
		} catch (final HibernateException ex) {
			GenericDAOImpl.LOG.error(ex);
			throw convertHibernateAccessException(ex);
		}
	}

	public List<T> listByHQL(String hql, Map<String, Object> params) {
		try {
			Query query = this.getHibernateTemplate().getSessionFactory()
					.getCurrentSession().createQuery(hql);
			if (params != null && !params.isEmpty()) {
				for (String key : params.keySet()) {
					query.setParameter(key, params.get(key));
				}
			}
			return query.list();
		} catch (final HibernateException ex) {
			GenericDAOImpl.LOG.error(ex);
			throw convertHibernateAccessException(ex);
		}
	}

	public T findByHQL(String hql, Map<String, Object> params) {
		try {
			Query query = this.getHibernateTemplate().getSessionFactory()
					.getCurrentSession().createQuery(hql);
			if (params != null && !params.isEmpty()) {
				for (String key : params.keySet()) {
					query.setParameter(key, params.get(key));
				}
			}
			return (T) query.uniqueResult();
		} catch (final HibernateException ex) {
			GenericDAOImpl.LOG.error(ex);
			throw convertHibernateAccessException(ex);
		}
	}

	public List<T> listAll(T entity) {
		try {
			return this.getHibernateTemplate().loadAll(getPersistentClass());
		} catch (final HibernateException ex) {
			GenericDAOImpl.LOG.error(ex);
			throw convertHibernateAccessException(ex);
		}

	}

	public T findById(int id) {
		try {
			return (T) this.getHibernateTemplate()
					.get(getPersistentClass(), id);
		} catch (final HibernateException ex) {
			GenericDAOImpl.LOG.error(ex);
			throw convertHibernateAccessException(ex);
		}

	}

	public T save(T entity) {
		try {
			this.getHibernateTemplate().save(entity);
			return entity;
		} catch (final HibernateException ex) {
			GenericDAOImpl.LOG.error(ex);
			throw convertHibernateAccessException(ex);
		}
	}

}
